<template>
  <el-dialog
    title="任务监听器"
    v-model="dialogVisible"
    width="900px"
    append-to-body
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    :show-close="false"
    @closed="$emit('close')"
  >
    <div class="flex flex-row items-center mb-2">
      <el-button type="primary" @click="handleAdd">新增</el-button>
      <el-button type="primary" @click="listenerDialogVisible = true">内置监听器</el-button>
    </div>
    <el-form ref="formRef" :model="formData" :rules="formRules" label-width="0">
      <el-table :data="formData.taskListener">
        <el-table-column width="180" align="center">
          <template #header>
            <el-tooltip effect="dark" placement="top-start">
              <template #content>
                <div>
                  create（创建）：当任务已经创建，并且所有任务参数都已经设置时触发。<br />
                  assignment（指派）：当任务已经指派给某人时触发。请注意：当流程执行到达用户任务时，在触发create事件之前，会首先触发assignment事件。<br />
                  complete（完成）：当任务已经完成，从运行时数据中删除前触发。<br />
                  delete（删除）：在任务即将被删除前触发。请注意任务由completeTask正常完成时也会触发。
                </div>
              </template>
              <span>事件</span>
            </el-tooltip>
          </template>
          <template #default="{ row, $index }">
            <el-form-item
              :prop="'taskListener.' + $index + '.event'"
              :rules="{ required: true, message: '请选择', trigger: ['blur', 'change'] }"
            >
              <el-select v-model="row.event">
                <el-option label="create" value="create" />
                <el-option label="assignment" value="assignment" />
                <el-option label="complete" value="complete" />
                <el-option label="delete" value="delete" />
              </el-select>
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column width="180" align="center">
          <template #header>
            <el-tooltip effect="dark" placement="top-start">
              <template #content>
                <div>
                  类：示例 com.company.MyCustomListener，自定义类必须实现
                  org.flowable.engine.delegate.TaskListener 接口 <br />
                  表达式：示例 ${myObject.callMethod(task, task.eventName)} <br />
                  委托表达式：示例 ${myListenerSpringBean} ，该 springBean 需要实现
                  org.flowable.engine.delegate.TaskListener 接口
                </div>
              </template>
              <span>类型</span>
            </el-tooltip>
          </template>
          <template #default="{ row, $index }">
            <el-form-item
              :prop="'taskListener.' + $index + '.type'"
              :rules="{ required: true, message: '请选择', trigger: ['blur', 'change'] }"
            >
              <el-select v-model="row.type">
                <el-option label="类" value="class" />
                <el-option label="表达式" value="expression" />
                <el-option label="委托表达式" value="delegateExpression" />
              </el-select>
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column label="值" align="center">
          <template #default="{ row, $index }">
            <el-form-item
              :prop="'taskListener.' + $index + '.className'"
              :rules="{ required: true, message: '请输入', trigger: ['blur', 'change'] }"
            >
              <el-input v-model="row.className" />
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column label="操作" align="center" width="60">
          <template #default="{ $index }">
            <el-icon size="26" color="red" @click="handleDel($index)">
              <CircleCloseFilled />
            </el-icon>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    <listener-list
      :visible="listenerDialogVisible"
      @close="
        () => {
          listenerDialogVisible = false;
        }
      "
      @submit="addListener"
    />
    <listenerParam
      v-if="showParamDialog"
      :value="formData.taskListener[nowIndex].params"
      @close="finishConfigParam"
    />
    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="closeDialog">确 定</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>
  import { ref, onMounted, defineProps } from 'vue';
  import mixinPanel from '../../../common/mixinPanel';
  import listenerParam from './listenerParam';
  import ListenerList from './ListenerList';

  const props = defineProps({
    modeler: {
      type: Object,
      required: true,
    },
    element: {
      type: Object,
      required: true,
    },
    categorys: {
      type: Array,
      default: () => [],
    },
  });

  const dialogVisible = ref(true);
  const listenerDialogVisible = ref(false);
  const showParamDialog = ref(false);
  const nowIndex = ref(null);
  const formRef = ref(null);
  const formData = ref({
    taskListener: [],
  });
  const formRules = ref({});

  const { updateProperties } = mixinPanel(props.modeler, props.element, formData);

  onMounted(() => {
    formData.value.taskListener =
      props.element.businessObject.extensionElements?.values
        .filter((item) => item.$type === 'flowable:TaskListener')
        .map((item) => {
          let type;
          if ('class' in item) type = 'class';
          if ('expression' in item) type = 'expression';
          if ('delegateExpression' in item) type = 'delegateExpression';
          return {
            event: item.event,
            type: type,
            className: item[type],
            params:
              item.fields?.map((field) => {
                let fieldType;
                if ('stringValue' in field) fieldType = 'stringValue';
                if ('expression' in field) fieldType = 'expression';
                return {
                  name: field.name,
                  type: fieldType,
                  value: field[fieldType],
                };
              }) ?? [],
          };
        }) ?? [];
  });

  // eslint-disable-next-line
  function configParam(index) {
    nowIndex.value = index;
    const nowObj = formData.value.taskListener[index];
    if (!nowObj.params) {
      nowObj.params = [];
    }
    showParamDialog.value = true;
  }

  function finishConfigParam(param) {
    showParamDialog.value = false;
    // hack 数量不更新问题
    const cache = formData.value.taskListener[nowIndex.value];
    cache.params = param;
    formData.value.taskListener.splice(nowIndex.value, 1, cache);
    // this.$set(formData.value.taskListener[nowIndex.value], nowIndex.value, cache);
    nowIndex.value = null;
  }

  function updateElement() {
    if (formData.value.taskListener?.length) {
      let extensionElements = props.element.businessObject.get('extensionElements');
      if (!extensionElements) {
        extensionElements = props.modeler.get('moddle').create('bpmn:ExtensionElements');
      }
      // 清除旧值
      extensionElements.values =
        extensionElements.values?.filter((item) => item.$type !== 'flowable:TaskListener') ?? [];
      formData.value.taskListener.forEach((item) => {
        const taskListener = props.modeler.get('moddle').create('flowable:TaskListener');
        taskListener['event'] = item.event;
        taskListener[item.type] = item.className;
        if (item.params && item.params.length) {
          item.params.forEach((field) => {
            const fieldElement = props.modeler.get('moddle').create('flowable:Field');
            fieldElement['name'] = field.name;
            fieldElement[field.type] = field.value;
            // 注意：flowable.json 中定义的string和expression类为小写，不然会和原生的String类冲突，此处为hack
            // const valueElement = props.modeler.get('moddle').create(`flowable:${field.type}`, { body: field.value })
            // fieldElement[field.type] = valueElement
            taskListener.get('fields').push(fieldElement);
          });
        }
        extensionElements.get('values').push(taskListener);
      });
      updateProperties({ extensionElements: extensionElements });
    } else {
      const extensionElements = props.element.businessObject[`extensionElements`];
      if (extensionElements) {
        extensionElements.values =
          extensionElements.values?.filter((item) => item.$type !== 'flowable:TaskListener') ?? [];
      }
    }
  }

  function closeDialog() {
    formRef.value.validate((valid) => {
      if (valid) {
        updateElement();
        dialogVisible.value = false;
      }
    });
  }

  function addListener(data) {
    formData.value.taskListener = formData.value.taskListener.concat(data);
  }

  function handleAdd() {
    formData.value.taskListener.push({
      event: null,
      type: null,
      className: null,
    });
  }

  function handleDel(idx) {
    formData.value.taskListener.splice(idx, 1);
  }
</script>

<style scoped lang="scss"></style>
